package com.maoshi.shop.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.maoshi.shop.bean.bo.PayInfoBo;
import com.maoshi.shop.bean.bo.RefundInfoBo;
import com.maoshi.shop.bean.dto.OrderRefundDto;
import com.maoshi.shop.common.enums.PayType;
import com.maoshi.shop.bean.pay.PayInfoDto;
import com.maoshi.shop.bean.pay.RefundInfoDto;
import com.maoshi.shop.common.bean.Alipay;
import com.maoshi.shop.common.config.Constant;
import com.maoshi.shop.common.exception.MaoshiShopBindException;
import com.maoshi.shop.common.util.Arith;
import com.maoshi.shop.common.util.IPHelper;
import com.maoshi.shop.service.PayManagerService;
import io.netty.util.CharsetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 统一支付工具
 * @author LGH
 */
@Service
public class PayManagerServiceImpl implements PayManagerService {

    @Autowired(required = false)
    private WxPayService wxMiniPayService;
    @Autowired(required = false)
    private WxPayService wxMpPayService;
    @Autowired(required = false)
    private AlipayClient alipayClient;
    @Autowired(required = false)
    private Alipay alipay;

    @Value("${api.domainName:}")
    private String apiDomainName;
    @Value("${multishop.domainName:}")
    private String multishopDomainName;

    @Value("${platform.multishopDomainName:}")
    private String platformMultishopDomainName;

    @Override
    public ResponseEntity<?> doPay(HttpServletResponse httpResponse, PayInfoDto payInfo) throws WxPayException, IOException {


        if (payInfo.getPayAmount() < 0.01 && payInfo.getIsScore() == 1) {
            return ResponseEntity.ok().build();
        }

        if (payInfo.getPayAmount() < 0.01) {
            throw new MaoshiShopBindException("订单金额有误，无法进行支付");
        }

        String notifyUrl = apiDomainName + payInfo.getApiNoticeUrl();

        if (Objects.equals(payInfo.getPayType(), PayType.WECHATPAY.value())
                || isPayWithMp(PayType.instance(payInfo.getPayType()))) {
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            orderRequest.setBody(payInfo.getBody());
            orderRequest.setOutTradeNo(payInfo.getPayNo());
            orderRequest.setTotalFee((int) Arith.mul(payInfo.getPayAmount(), 100));
            orderRequest.setSpbillCreateIp(IPHelper.getIpAddr());
            orderRequest.setOpenid(payInfo.getBizUserId());

            orderRequest.setNotifyUrl(notifyUrl);

            // 微信小程序支付
            if (Objects.equals(payInfo.getPayType(), PayType.WECHATPAY.value())) {
                orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
                WxPayMpOrderResult wxPayMpOrderResult = wxMiniPayService.createOrder(orderRequest);
                return ResponseEntity.ok(wxPayMpOrderResult);
            }
            // 微信网页支付
            else if (Objects.equals(payInfo.getPayType(), PayType.WECHATPAY_SWEEP_CODE.value())) {
                orderRequest.setProductId(payInfo.getPayNo());
                // 生成微信二维码
                orderRequest.setTradeType(WxPayConstants.TradeType.NATIVE);
                WxPayNativeOrderResult wxPayNativeOrderResult = wxMpPayService.createOrder(orderRequest);

                return ResponseEntity.ok(wxPayNativeOrderResult.getCodeUrl());
            }
            // 微信H5支付
            else if (Objects.equals(payInfo.getPayType(), PayType.WECHATPAY_H5.value())) {
                // 生成微信二维码
                orderRequest.setTradeType(WxPayConstants.TradeType.MWEB);
                String s = "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"\",\"wap_name\": \"\"}}";
                orderRequest.setSceneInfo(s);
                WxPayMwebOrderResult wxPayMwebOrderResult = wxMpPayService.createOrder(orderRequest);
                return ResponseEntity.ok(wxPayMwebOrderResult.getMwebUrl());
            }
            // 微信公众号支付
            else if (Objects.equals(payInfo.getPayType(), PayType.WECHATPAY_MP.value())) {
                orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
                WxPayMpOrderResult wxPayMpOrderResult = wxMpPayService.createOrder(orderRequest);
                return ResponseEntity.ok(wxPayMpOrderResult);
            }
        } else if (Objects.equals(payInfo.getPayType(), PayType.ALIPAY.value())) {
            //创建API对应的request
            AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

            //在公共参数中设置回跳和通知地址
            alipayRequest.setNotifyUrl(notifyUrl);
            AlipayTradePagePayModel alipayTradePagePayModel = new AlipayTradePagePayModel();
            alipayTradePagePayModel.setOutTradeNo(payInfo.getPayNo());
            alipayTradePagePayModel.setBody(payInfo.getBody());
            alipayTradePagePayModel.setSubject(payInfo.getBody());
            alipayTradePagePayModel.setTotalAmount(Double.toString(payInfo.getPayAmount()));
            alipayTradePagePayModel.setProductCode("FAST_INSTANT_TRADE_PAY");
            alipayRequest.setBizModel(alipayTradePagePayModel);

            String form="";
            try {
                //调用SDK生成表单
                form = alipayClient.pageExecute(alipayRequest).getBody();
            } catch (AlipayApiException e) {
                e.printStackTrace();
            }
            httpResponse.setContentType("text/html;charset=" + CharsetUtil.UTF_8);
            //直接将完整的表单html输出到页面
            httpResponse.getWriter().write(form);
            httpResponse.getWriter().flush();
            httpResponse.getWriter().close();
        }
        return null;
    }

    @Override
    public PayInfoBo validateAndGetPayInfo(HttpServletRequest request, PayType payType, String xmlData) throws UnsupportedEncodingException, WxPayException, AlipayApiException {

        PayInfoBo payInfoBo = new PayInfoBo();
        // 小程序支付
        if (Objects.equals(payType, PayType.WECHATPAY)) {
            WxPayOrderNotifyResult parseOrderNotifyResult = wxMiniPayService.parseOrderNotifyResult(xmlData);
            payInfoBo.setPayNo(parseOrderNotifyResult.getOutTradeNo());
            payInfoBo.setBizPayNo(parseOrderNotifyResult.getTransactionId());
        }
        else if (isPayWithMp(payType)) {
            WxPayOrderNotifyResult parseOrderNotifyResult = wxMpPayService.parseOrderNotifyResult(xmlData);
            payInfoBo.setPayNo(parseOrderNotifyResult.getOutTradeNo());
            payInfoBo.setBizPayNo(parseOrderNotifyResult.getTransactionId());
        } else if (Objects.equals(payType, PayType.ALIPAY)) {
            //乱码解决，这段代码在出现乱码时使用
            request.setCharacterEncoding("utf-8");
            //获取支付宝POST过来反馈信息
            Map<String,String> params = new HashMap<>(16);
            Map<String,String[]> requestParams = request.getParameterMap();
            for(String name :requestParams.keySet()){
                String[] values = requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i]
                            : valueStr + values[i] + ",";
                }
                params.put(name, valueStr);
            }
            //调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCertCheckV1(params, alipay.getAlipayCertPath(), cn.hutool.core.util.CharsetUtil.UTF_8, AlipayConstants.SIGN_TYPE_RSA2);

            if(!signVerified) {
                throw new MaoshiShopBindException("验签失败");
            }

            //商户订单号,之前生成的带用户ID的订单号
            payInfoBo.setPayNo(params.get("out_trade_no"));
            //支付宝交易号
            payInfoBo.setBizPayNo(params.get("trade_no"));
        }
        payInfoBo.setIsPaySuccess(true);
        return payInfoBo;


    }

    @Override
    public PayInfoBo getPayInfo(PayType payType, String payNo) {
        PayInfoBo payInfo = new PayInfoBo();
        payInfo.setPayNo(payNo);

        try {
            if (isPayWithMp(payType) || Objects.equals(payType, PayType.WECHATPAY)) {
                WxPayOrderQueryResult wxPayOrderQueryResult = null;
                if (isPayWithMp(payType)) {
                    wxPayOrderQueryResult = wxMpPayService.queryOrder(null, payNo);
                } else if (Objects.equals(payType, PayType.WECHATPAY)) {
                    wxPayOrderQueryResult = wxMiniPayService.queryOrder(null, payNo);
                }

                if(!Objects.isNull(wxPayOrderQueryResult) && Objects.equals(wxPayOrderQueryResult.getTradeState(), Constant.SUCCESS)){
                    payInfo.setBizPayNo(wxPayOrderQueryResult.getTransactionId());
                    payInfo.setIsPaySuccess(true);
                }else {
                    payInfo.setIsPaySuccess(false);
                }
            }

        } catch (WxPayException e) {
            e.printStackTrace();
            payInfo.setIsPaySuccess(false);
        }
        return payInfo;
    }

    @Override
    public void doRefund(RefundInfoDto refundInfo) {
        // 退款操作
        // 生成退款请求对象
        WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
        // 商户订单号
        wxPayRefundRequest.setOutTradeNo(refundInfo.getPayNo());
        // 订单金额
        double totalFee = Arith.mul(refundInfo.getPayAmount(), 100);
        wxPayRefundRequest.setTotalFee((int) totalFee);
        // 退款金额
        double refundFee = Arith.mul(refundInfo.getRefundAmount(), 100);
        wxPayRefundRequest.setRefundFee((int) refundFee);
        // 退款编码
        wxPayRefundRequest.setOutRefundNo(refundInfo.getRefundSn());
        String domainName = multishopDomainName;
        if (StrUtil.isBlank(domainName)) {
            domainName = platformMultishopDomainName;
        }

        // notifyUrl（通知结果）
        wxPayRefundRequest.setNotifyUrl(domainName + refundInfo.getNotifyUrl());

        // 提交提款操作
        try {
            if (isPayWithMp(refundInfo.getPayType())) {
                wxMpPayService.refund(wxPayRefundRequest);
            } else if (Objects.equals(refundInfo.getPayType(), PayType.WECHATPAY)) {
                wxMiniPayService.refund(wxPayRefundRequest);
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            throw new MaoshiShopBindException(e.getCustomErrorMsg(), e);
        }
    }

    @Override
    public RefundInfoBo validateAndGetRefundInfo(HttpServletRequest request, PayType payType, String xmlData) throws UnsupportedEncodingException, WxPayException, AlipayApiException {

        RefundInfoBo refundInfoBo = new RefundInfoBo();

        WxPayRefundNotifyResult wxPayRefundNotifyResult = null;

        if (isPayWithMp(payType)) {
            wxPayRefundNotifyResult = wxMpPayService.parseRefundNotifyResult(xmlData);
        } else if (Objects.equals(payType, PayType.WECHATPAY)) {
            wxPayRefundNotifyResult = wxMiniPayService.parseRefundNotifyResult(xmlData);
        }
        WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo();

        //
        if ("SUCCESS".equals(reqInfo.getRefundStatus())) {
            refundInfoBo.setIsRefundSuccess(true);
            refundInfoBo.setRefundNo(reqInfo.getOutRefundNo());
            refundInfoBo.setBizRefundNo(reqInfo.getRefundId());
        } else {
            refundInfoBo.setIsRefundSuccess(false);
        }

        return refundInfoBo;
    }

    private boolean isPayWithMp(PayType payType) {
        return (Objects.equals(payType, PayType.WECHATPAY_SWEEP_CODE)
                || Objects.equals(payType, PayType.WECHATPAY_H5)
                || Objects.equals(payType, PayType.WECHATPAY_MP));
    }

}
